Skip to content

Conversation

@calvin-codecov
Copy link
Contributor

@calvin-codecov calvin-codecov commented Feb 10, 2026

Adds general command for the team to run to bulk add subscription schedules for Owners. It will procure the matching owners based on the arguments and process each.

Takes these as user provided arguments:

  • end date
  • target plan (if it is switching plans)
  • dry-run
  • renew-date-gte
  • renew-date-lte
  • owner ids array allowlist
  • current plan that we want to filter owners by
  • owner ids array exclude list
  • limit for # owners to run for
  • ownerid cursor for batching

Handles multiple scenarios for each owner

  • if this subscription already has had this applied and the dates match up, skip
  • if the dates don't match up, update the date
  • if this subscription has a unrelated schedule, add to the schelude
  • create a subscription schedule if it doesn't have a schedule already

The command will return a summary of what happened and the owner id for the last acted upon owner.

PR also adds to the modify_subscription webhook handler logic to reapply the schedule if the subscription encounters an upgrade after the command has been run as upgrades will release schedules.


Note

Medium Risk
Touches Stripe subscription upgrade and scheduling logic; mistakes could incorrectly cancel, extend, or drop cancellation schedules for paying customers. Changes are test-covered but interact with live Stripe state and metadata conventions.

Overview
Adds a new apply_subscription_schedules Django management command to bulk create/update Stripe SubscriptionSchedules that cancel subscriptions at a specified end date, with filters (renewal date, plan, owner allow/deny lists), cursor-based batching, and a --dry-run mode.

Introduces shared schedule task_signature constants and reuses a new helper (_create_end_date_schedule) to make schedule creation idempotent and distinguish schedules created by the command/webhook. Updates StripeService.modify_subscription upgrade flow to detect and preserve prior cancellation end dates when releasing schedules (recreate after successful upgrade, and restore on upgrade failure). Adds comprehensive tests for the command and the new upgrade/schedule-recreation behavior.

Written by Cursor Bugbot for commit f36f356. This will update automatically on new commits. Configure here.

@calvin-codecov calvin-codecov force-pushed the cy/stripe_yearly_sub_sched_action branch from a1aafd4 to 7e2f900 Compare February 10, 2026 23:20
@calvin-codecov calvin-codecov changed the title feat: Add command for adding end dates to annual plans feat: Add command for adding schedules to subscriptions Feb 10, 2026
Comment on lines -349 to -350
# If the user is not in a schedule, update immediately
# If the user is in a schedule, update the existing schedule
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was actually already no longer correct and had not been updated. This handler has been releasing the existing schedule for a long time.

@calvin-codecov calvin-codecov force-pushed the cy/stripe_yearly_sub_sched_action branch from 938002c to f36f356 Compare February 12, 2026 17:52
@sentry
Copy link

sentry bot commented Feb 12, 2026

Codecov Report

❌ Patch coverage is 78.46154% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.20%. Comparing base (cf5ee52) to head (f36f356).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...anagement/commands/apply_subscription_schedules.py 77.51% 38 Missing ⚠️
apps/codecov-api/services/billing.py 83.33% 4 Missing ⚠️

❌ Your patch check has failed because the patch coverage (78.46%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #707      +/-   ##
==========================================
- Coverage   92.25%   92.20%   -0.06%     
==========================================
  Files        1302     1303       +1     
  Lines       47854    48047     +193     
  Branches     1628     1628              
==========================================
+ Hits        44149    44300     +151     
- Misses       3396     3438      +42     
  Partials      309      309              
Flag Coverage Δ
apiunit 96.16% <78.46%> (-0.20%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@codecov-notifications
Copy link

Codecov Report

❌ Patch coverage is 78.46154% with 42 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...anagement/commands/apply_subscription_schedules.py 77.51% 38 Missing ⚠️
apps/codecov-api/services/billing.py 83.33% 4 Missing ⚠️

❌ Your patch check has failed because the patch coverage (78.46%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

📢 Thoughts on this report? Let us know!

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

phase1_quantity=desired_plan["quantity"],
subscription=subscription,
task_signature=WEBHOOK_CANCELLATION_TASK_SIGNATURE,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schedule recreation failure prevents DB plan update

Medium Severity

_create_end_date_schedule is called between the successful Subscription.modify and plan_service.update_plan. If the schedule recreation fails (Stripe API error, network issue), the exception propagates, skipping the DB update. This leaves the subscription upgraded in Stripe but the owner's plan stale in the database, and the previously released cancellation schedule is permanently lost.

Additional Locations (1)

Fix in Cursor Fix in Web

)
from billing.management.commands.apply_subscription_schedules import (
_create_end_date_schedule,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shared utility function lives inside management command

Low Severity

_create_end_date_schedule is defined as a private function (underscore prefix) inside a management command module but is imported by production service code in billing.py. This inverts the expected dependency direction — services typically feed management commands, not vice versa. The function belongs in a shared module like billing/utils.py or billing/services.py.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant